package com.qclix.inv.pdf;

import com.qclix.inv.*;
import com.qclix.inv.xml.XMLTranslationException;
import com.qclix.inv.xml.XMLTranslator;
import org.apache.fop.apps.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.util.Locale;

/**
 * PDF implementation based on Apache FOP
 */
public class FOPPDFCreator implements PDFCreator {
    private static Logger logger = LoggerFactory.getLogger(FOPPDFCreator.class);
    private XMLTranslator xmlTranslator;

    public void setXmlTranslator(XMLTranslator xmlTranslator) {
        this.xmlTranslator = xmlTranslator;
    }

    public byte[] getPDF(Invoice invoice) {
        try {
            if (invoice.getClient() == null) {
                if (invoice.getContract() == null) {
                    logger.error("For " + invoice + ": both client and contract are null");
                    throw new RuntimeException("For " + invoice + ": both client and contract are null");
                }
                // TODO: this client (direct or from contract) should be handled in the FOP
                invoice.setClient(invoice.getContract().getClient());
            }

            if (invoice.getClient().getStreet2() == null) {
                invoice.getClient().setStreet2("");
            }
            String xml = xmlTranslator.toXML(invoice);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            transformToPDF(xml, out, this.getClass().getResourceAsStream("/invoiceFO.xsl"));
            out.close();
            return out.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (XMLTranslationException e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] getPDF(Timesheet timesheet) {
        try {
            String xml = xmlTranslator.toXML(timesheet);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
            Locale.setDefault(Locale.FRENCH);
            transformToPDF(xml, out, this.getClass().getResourceAsStream("/timesheetFO.xsl"));
            out.close();
            return out.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void transformToPDF(String xml, OutputStream out, InputStream xslfo) {
        Source xmlSource = new StreamSource(new StringReader(xml));
        Source xsltSource = new StreamSource(xslfo);
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer;
        try {
            transformer = transformerFactory.newTransformer(xsltSource);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        transformer.setParameter("versionParam", "2.0");
        transformer.setOutputProperty("encoding", "UTF-16");

        FopFactory fopFactory = FopFactory.newInstance();
        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
        foUserAgent.setURIResolver(new ClassPathURIResolver());
        try {
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
            Result result = new SAXResult(fop.getDefaultHandler());
            transformer.transform(xmlSource, result);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
